package com.bdcom.comp;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.imageio.ImageIO;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.PrintSetup;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.bdcom.listener.CanvasMouseListener;
import com.bdcom.model.AbstractBarrier;
import com.bdcom.model.BarrierType;
import com.bdcom.model.CanvasModel;
import com.bdcom.model.CurveBarrier;
import com.bdcom.model.Device;
import com.bdcom.model.DeviceType;
import com.bdcom.model.EllipseBarrier;
import com.bdcom.model.IBarrier;
import com.bdcom.model.IDevice;
import com.bdcom.model.LineBarrier;
import com.bdcom.model.RectBarrier;
import com.bdcom.planner.ApplicationFrame;
import com.bdcom.util.MapRender;
import com.bdcom.util.PlannerUtil;


/**
 * 画布面板
 * @author Guodeqi
 *
 */
@Component
public class CanvasPanel extends JPanel { 
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	//记录临时点列表
	private List<Point> tmpPointList = new ArrayList<>();
	//提示点
	private Point tipPoint = null;
	//移动点
	private Point movePoint=null;
	
	private Object selectedObject=null;
	
	private int selectedPointIndex=-1;
	//当前工程模板
	private CanvasModel canvasModel=null;
	@Autowired
	private InfoBarPanel infoBarPanel;
	@Autowired
	private CanvasMouseListener canvasMouseListener;
	@Autowired
	private PropertyPanel propertyPanel;
	@Autowired
	private ToolBarPanel toolBarPanel;
	@Autowired
	private ApplicationFrame applicationFrame;
	
	private int canvasWidth;
	
	private int canvasHeight;
	
	//sub getter and setter
	public CanvasModel getCanvasModel() {
		return canvasModel;
	}
	
	public void setCanvasModel(CanvasModel canvasModel){
		this.canvasModel = canvasModel;
		//设置前清空零时数据
		clearTempDatas();
		if(canvasModel==null){
			infoBarPanel.setProjectName("");
			toolBarPanel.setEnabled(false);
			propertyPanel.setEnabled(false);
		}
		else{
			infoBarPanel.setProjectName(canvasModel.getProjectName());
			toolBarPanel.setEnabled(true);
			propertyPanel.setEnabled(true);
		}
		resizeScroll();
	}
	
	public List<Point> getTmpPointList() {
		return tmpPointList;
	}
	
	public void setTmpPointList(List<Point> tmpPointList) {
		this.tmpPointList = tmpPointList;
	}
	
	public Point getMovePoint() {
		return movePoint;
	}
	
	public void setMovePoint(Point movePoint) {
		this.movePoint = movePoint;
	}
	
	public Object getSelectedObject() {
		return selectedObject;
	}
	
	public void setSelectedObject(Object selectedObject) {
		this.selectedObject = selectedObject;
	
		setSelectedPointIndex(-1);	
		
		propertyPanel.setProperties(selectedObject);
		
		if (selectedObject!=null) {
			toolBarPanel.setEnabledelMenu(true);
		}
	}
	
	public int getSelectedPointIndex() {
		return selectedPointIndex;
	}
	
	public void setSelectedPointIndex(int selectedPointIndex) {
		this.selectedPointIndex = selectedPointIndex;

		toolBarPanel.setEnablecombinMenu(selectedPointIndex!=-1);
	}
	
	public Point getTipPoint() {
		return tipPoint;
	}
	
	public void setTipPoint(Point tipPoint) {
		this.tipPoint = tipPoint;
	}
	
	public int getCanvasWidth() {
		return canvasWidth;
	}

	public void setCanvasWidth(int canvasWidth) {
		this.canvasWidth = canvasWidth;
	}

	public int getCanvasHeight() {
		return canvasHeight;
	}

	public void setCanvasHeight(int canvasHeight) {
		this.canvasHeight = canvasHeight;
	}

	//end sub getter and setter
	@Override
	public void paintComponent(Graphics g) {
		
		super.paintComponent(g);
		
		if(canvasModel==null){
		//当模型为空不做任何事			
			return;
		}
		Graphics2D g2d=(Graphics2D) g;
		//设置画板比例尺
		g2d.scale(canvasModel.getRoomPercent(),canvasModel.getRoomPercent());
		
		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		
		try {
			drawCanvasBack(g2d);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			infoBarPanel.setInfo(PlannerUtil.getString("Draw canvas backgroundImage Exception"));
		}
		
		try {
			
			drawDevices(g2d);
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			infoBarPanel.setInfo("draw device Exception.");
		}
		
		drawBarriers(g2d);
		
		drawTempPoint(g2d);

		drawTipPoint(g2d);
		
	}
	//添加鼠标事件
	@PostConstruct
	private void init(){
		addMouseListener(canvasMouseListener);
		addMouseMotionListener(canvasMouseListener);
	}
	//绘制提示点
	private void drawTipPoint(Graphics2D g2d) {
		// TODO Auto-generated method stub
		if (tipPoint==null) {
			return;
		}
		g2d.drawOval(tipPoint.x-4,tipPoint.y-4, 8, 8);
	}
	//绘制临时点
	private void drawTempPoint(Graphics2D g2d) {
		// TODO Auto-generated method stub
		g2d.setColor(PlannerUtil.TEMP_POINT_COLOR);
		
		switch (canvasMouseListener.getListenerCommand()) {
		case ADDLINE:
		case ADDCURVE:
			{
				if(tmpPointList.size()>0&&movePoint!=null){
					
					int nPoints=tmpPointList.size()+1;
					
					int[] xPoints=new int[nPoints];
					
					int[] yPoints=new int[nPoints];
					
					int cursor=0;
					
					for (Point p : tmpPointList) {
						
						xPoints[cursor]=p.x;
						
						yPoints[cursor]=p.y;
						
						cursor++;
					}
					
					xPoints[cursor]=movePoint.x;
					yPoints[cursor]=movePoint.y;
					
					g2d.drawPolyline(xPoints, yPoints, nPoints);
					
				}
			}
		break;
		case ADDRECT:
		{
			if(tmpPointList.size()==1&&movePoint!=null){
				
				Point A=tmpPointList.get(0);
				
				g2d.drawRect(Math.min(A.x, movePoint.x),Math.min(A.y, movePoint.y),Math.abs(A.x-movePoint.x),Math.abs(A.y-movePoint.y));
				
			}
		}
		break;
		case ADDELLIPSE:
		{
			if(tmpPointList.size()==1&&movePoint!=null){
				
				Point A=tmpPointList.get(0);
				
				g2d.drawOval(Math.min(A.x, movePoint.x),Math.min(A.y, movePoint.y),Math.abs(A.x-movePoint.x),Math.abs(A.y-movePoint.y));
				
			}
		}
		break;
		default:
			break;
		}
		
	}
	//绘制障碍物
	private void drawBarriers(Graphics2D g2d) {
		// TODO Auto-generated method stub
		
		for(IBarrier barrier:canvasModel.getBarriers()){
			
			if(barrier!=selectedObject){
				
				barrier.drawBarrier(g2d,/* roomPercent,*/false);
			}else
			{
				barrier.drawBarrier(g2d,/* roomPercent,*/true);
			}
		}
		if(selectedPointIndex!=-1){
			
			Point point=((AbstractBarrier)selectedObject).getPoints().get(selectedPointIndex);
			g2d.setColor(Color.RED);
			g2d.fillOval(point.x-5, point.y-5, 10, 10);
			
		}
		
	}
	//绘制设备
	private void drawDevices(Graphics2D g2d) throws Exception {
		// TODO Auto-generated method stub
		
		for(IDevice device:canvasModel.getDevs())
		{
			
			if (device!=selectedObject)
			{
				device.drawDevice(g2d,false);
			}else
			{
				device.drawDevice(g2d,true);
			}
			
			
		}
		
	}
	//绘制画布背景和尺寸
	private void drawCanvasBack(Graphics2D g) throws Exception{
		// TODO Auto-generated method stub
		
		if(canvasModel.getBackImg()!=null){
			
			 canvasWidth = canvasModel.getBackImg().getWidth();
			
			 canvasHeight =canvasModel.getBackImg().getHeight();
			
			this.setPreferredSize(new Dimension((int)(canvasWidth*canvasModel.getRoomPercent()), (int)(canvasHeight*canvasModel.getRoomPercent())));
			
			g.drawImage(canvasModel.getBackImg(), 0, 0,  canvasWidth , canvasHeight , null);
		}
		else if(canvasModel.getBackImgPath()!=null&&canvasModel.getBackImgPath().length()>0){
			
			BufferedImage backImg;
			
				backImg = ImageIO.read(new File(canvasModel.getBackImgPath()));
				
				canvasModel.setBackImg(backImg);
				
				this.repaint();
			
		}else
		{
			 canvasWidth =PlannerUtil.DEFAULT_CANVAS_WIDTH;
			
			 canvasHeight =PlannerUtil.DEFAULT_CANVAS_HEIGHT;
			
			this.setPreferredSize(new Dimension((int)(canvasWidth*canvasModel.getRoomPercent()), (int)(canvasHeight*canvasModel.getRoomPercent())));
			
			g.setColor(Color.RED);
			
			g.drawRect(0, 0, canvasWidth-1, canvasHeight-1);
			
			infoBarPanel.setInfo(PlannerUtil.getString("Please input an image for design"));
		}
		
	}
	//清除临时数据
	public void clearTempDatas(){
		
		tmpPointList.clear();
		
		tipPoint=null;
		
		movePoint=null;
		
		setSelectedObject(null);
		
		//setSelectedPointIndex(-1);
		
		toolBarPanel.setEnabledelMenu(false);
		
	}
	//判断该点是否在已有的障碍物上
	public boolean validatePoint(Point point){
		
		for(IBarrier barrier:canvasModel.getBarriers()){
			
			if(barrier==selectedObject){
				
				continue;
			}
			if( barrier.containsPoint(point)){
				
				return true;
			}
		}
		
		return false;
	}
	//删除选中的对象
	public void removeSelected(){
		if(selectedPointIndex!=-1){
			if(((AbstractBarrier)selectedObject).getPoints().size()==2){
				if(JOptionPane.showConfirmDialog(null,  PlannerUtil.getString("move this point will move the graph"),PlannerUtil.getString("are you sure to delete this graph")+"?",JOptionPane.YES_NO_OPTION)==JOptionPane.OK_OPTION){
					canvasModel.getBarriers().remove(selectedObject);
				}
			}else
			{
				((AbstractBarrier)selectedObject).removePoint(selectedPointIndex);
			}
		}
		else if(selectedObject instanceof IDevice)
		{
			canvasModel.getDevs().remove(selectedObject);
			
		}else if(selectedObject instanceof IBarrier)
		{
			canvasModel.getBarriers().remove(selectedObject);
		}
		
		setSelectedObject(null);
		
		this.repaint();
	}
	//选择背景图
	public void chooseBackGroundImage(){
		
		JFileChooser fileChooser = new JFileChooser();
		
		fileChooser.setCurrentDirectory(new File("./image"));
		
		fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
		
		fileChooser.setDialogTitle(PlannerUtil.getString("Please choose the design image"));
		
		int showOpenDialog = fileChooser.showOpenDialog(this);
		
		if (showOpenDialog == JFileChooser.APPROVE_OPTION) {
		
			String path = fileChooser.getSelectedFile().getAbsolutePath();
			
			canvasModel.setBackImgPath(path);
			
			propertyPanel.repaint();
			
		}
		this.repaint();
	}
	//获取比例尺
	public double getScale(){
	
		try {
			return canvasModel.getMapWidth()/canvasModel.getBackImg().getWidth()*PlannerUtil.DPCM/canvasModel.getRoomPercent();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			return 0;
		}
	
	}
	//重置画布
	public void resetCanvas() {
		// TODO Auto-generated method stub
		clearTempDatas();
		canvasModel.getDevs().clear();
		canvasModel.getBarriers().clear();
		canvasModel.setBackImgPath(null);
		canvasModel.setRoomPercent(1.0D);
		canvasModel.setMinSign(-85);
		canvasModel.setMaxSign(-10);
		canvasModel.setMinSignToShow(-70);
		propertyPanel.repaint();
		this.repaint();
		
	}

	public void combinPoints() {
		// TODO Auto-generated method stub
		if(getSelectedPointIndex()==-1){
			
			return;
			
		};
		
		Point selectedp=((AbstractBarrier)getSelectedObject()).getPoints().get(getSelectedPointIndex());
		
		for(IBarrier barrier:canvasModel.getBarriers()){
			
			if(barrier==selectedObject){
				//如果是障碍物自己
				List<Point> list=((AbstractBarrier)barrier).getPoints();
				
				int size=list.size();
				
				for(int i=0;i<size;i++){
					//如果点是自己
					if(i==getSelectedPointIndex()){
						
						continue;
						
					}
					
					if(list.get(i).distance(selectedp)<=20){
						
						selectedp.setLocation(list.get(i));
						
						repaint();
						
						return;
					}
					
				}
				
			}
			
			for(Point p:((AbstractBarrier)barrier).getPoints()){
				
				if(p.distance(selectedp)<=20){
					
					selectedp.setLocation(p);
					
					repaint();
					
					return;
				}
				
			}
			
		}
		
	}

	public boolean exportDevicesAsXls(File file) {
		// TODO Auto-generated method stub
		return exportExcel(canvasModel.getDevs(), file);
	}

	private boolean exportExcel(List<IDevice> devlist, File file) {
		// TODO Auto-generated method stub

		FileOutputStream out=null;
		
		Workbook wb =null;;
		
		if(file.getName().endsWith(".xls"))
		{
			
			wb=new HSSFWorkbook();
			
		}else
		if(file.getName().endsWith(".xlsx"))
		{
			
			wb=new XSSFWorkbook();
			
		}
		
		if(wb==null){
			
			return false;
		}
		
		Map<String, CellStyle> styles=createStyle(wb);
		
		Sheet sheet= wb.createSheet(PlannerUtil.getString("device info"));
		
		sheet.setDisplayGridlines(false);
		
		sheet.setPrintGridlines(false);
		
		sheet.setFitToPage(true);
		
		sheet.setHorizontallyCenter(true);
		
		PrintSetup printsetup=sheet.getPrintSetup();
		
		printsetup.setLandscape(true);
		
		sheet.setAutobreaks(true);
		
		printsetup.setFitHeight((short)1);
		
		printsetup.setFitWidth((short)1);
		
		Row headerrow=sheet.createRow(0);
		
		headerrow.setHeightInPoints(12.75f);
		
		for(int i=0;i<PlannerUtil.headers.length;i++){
			
			Cell cell=headerrow.createCell(i);
			
			cell.setCellValue(PlannerUtil.headers[i]);
			
			cell.setCellStyle(styles.get("header"));
			
		}
		
		sheet.createFreezePane(0, 1);
		
		Row row;
		
		Cell cell;
		
		Device device;
		
		int count=devlist.size();
		
		CellStyle c_style=styles.get("cell-default");
		
		for(int j=1;j<=count;j++){
			
			row=sheet.createRow(j);
			
			device=(Device)devlist.get(j-1);
			
			cell=row.createCell(0);
			
			cell.setCellValue(device.getDeviceName());
			
			cell.setCellStyle(c_style);
			
			cell=row.createCell(1);
			
			cell.setCellValue(device.getMacAddress());
			
			cell.setCellStyle(c_style);
			
			cell=row.createCell(2);
			
			cell.setCellValue(device.getDeviceType().getTypeName());
			
			cell.setCellStyle(c_style);
			
		}
		
		sheet.setColumnWidth(0, 256*10);
		
		sheet.setColumnWidth(1, 256*20);
		
		sheet.setColumnWidth(2, 256*10);
		
		try {
			out=new FileOutputStream(file);
			
			wb.write(out);
			
			
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			
			e.printStackTrace();
			
			return false;
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			
			return false;
			
		}finally{

			try {
				wb.close();
				out.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
		
		return true;
	}

	private Map<String, CellStyle> createStyle(Workbook wb) {
		// TODO Auto-generated method stub
		Map<String,CellStyle> styles=new HashMap<>();
		
		CellStyle cellstyle;
		
		Font header_font=wb.createFont();
		
		header_font.setBold(true);
		
		BorderStyle thin=BorderStyle.THIN;
		
		short black=IndexedColors.BLACK.getIndex();
		
		cellstyle=wb.createCellStyle();
		
		cellstyle.setBorderRight(thin);
		
		cellstyle.setRightBorderColor(black);
		
		cellstyle.setBorderLeft(thin);
		
		cellstyle.setRightBorderColor(black);
		
		cellstyle.setBorderTop(thin);
		
		cellstyle.setTopBorderColor(black);
		
		cellstyle.setBorderBottom(thin);
		
		cellstyle.setBottomBorderColor(black);
		
		cellstyle.setAlignment(HorizontalAlignment.CENTER);
		
		cellstyle.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.index);
		
		cellstyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
		
		cellstyle.setFont(header_font);
		
		styles.put("header",cellstyle);
		
		Font cell_font=wb.createFont();
		
		cell_font.setBold(false);
		
		cellstyle=wb.createCellStyle();
		
		cellstyle.setBorderRight(thin);
		
		cellstyle.setRightBorderColor(black);
		
		cellstyle.setBorderLeft(thin);
		
		cellstyle.setLeftBorderColor(black);
		
		cellstyle.setBorderTop(thin);
		
		cellstyle.setTopBorderColor(black);
		
		cellstyle.setBorderBottom(thin);
		
		cellstyle.setBottomBorderColor(black);
		
		cellstyle.setAlignment(HorizontalAlignment.CENTER);
		
		cellstyle.setFont(cell_font);
		
		styles.put("cell-default", cellstyle);
		
		return styles;
		
	}

	public void addDevice(Point point) {
		// TODO Auto-generated method stub
		Device device = new Device();
		// 名称[默认为AP-1-1]最后一个值递增
		device.setDeviceName("AP-1-" + getCanvasModel().getDevs().size());
		// 设置mac地址
		device.setMacAddress("0000-0000-0000");

		device.setPoint(point);
		//设置设备类型
		device.setDeviceType((DeviceType) ToolBarPanel.devCombobox.getSelectedItem());

		getCanvasModel().getDevs().add(device);
		
		repaint();
	}

	public void addLine() {
		// TODO Auto-generated method stub
		if (getTmpPointList().size()<2) {
			
			infoBarPanel.setInfo(PlannerUtil.getString("Draw a line needs 2 or more points"));
			
			return;
		}
		LineBarrier lineBarrier = new LineBarrier();

		lineBarrier.getPoints().addAll(getTmpPointList());
		
		//设置障碍物。
		
		lineBarrier.setBarrierType((BarrierType) ToolBarPanel.barrierCombobox.getSelectedItem());

		getCanvasModel().getBarriers().add(lineBarrier);

		clearTempDatas();
		
		repaint();
	}

	public void addTempPoint(Point point) {
		// TODO Auto-generated method stub
		getTmpPointList().add(point);
		
		repaint();
	}

	public void addCurve() {
		// TODO Auto-generated method stub
		
		if (getTmpPointList().size()<3) {
			
			infoBarPanel.setInfo(PlannerUtil.getString("Draw a curve needs 3 or more points"));
			
				return;
		}
		
		CurveBarrier curveBarrier = new CurveBarrier();

		curveBarrier.getPoints().addAll(getTmpPointList());
		
		curveBarrier.setBarrierType((BarrierType) ToolBarPanel.barrierCombobox.getSelectedItem());

		getCanvasModel().getBarriers().add(curveBarrier);

		clearTempDatas();
		
		repaint();
	}
	//添加一个矩形障碍物
	public void addRect() {
		// TODO Auto-generated method stub
		RectBarrier rectBarrier = new RectBarrier();

		rectBarrier.getPoints().addAll(getTmpPointList());
		
		//设置障碍物。		
		rectBarrier.setBarrierType((BarrierType) ToolBarPanel.barrierCombobox.getSelectedItem());

		getCanvasModel().getBarriers().add(rectBarrier);
		
		clearTempDatas();
		
		repaint();
	}
	//添加一个圆形障碍物
	public void addEllipse() {
		// TODO Auto-generated method stub
		EllipseBarrier ellipseBarrier = new EllipseBarrier();

		ellipseBarrier.getPoints().addAll(getTmpPointList());
		
		//设置障碍物。		
		ellipseBarrier.setBarrierType((BarrierType) ToolBarPanel.barrierCombobox.getSelectedItem());

		getCanvasModel().getBarriers().add(ellipseBarrier);
		
		clearTempDatas();
		
		repaint();
	}
	//自动布局界面
	public void autoLayout() {
		// TODO Auto-generated method stub
		//先计算所选类型设备的最远辐射距离
		//根据平面图的大小计算需要多少个设备能完全覆盖平面图
		//然后自动添加设备
		//布局之前删除所有设备
		canvasModel.getDevs().clear();
		
		DeviceType deviceType=(DeviceType) ToolBarPanel.devCombobox.getSelectedItem();
		
		if (deviceType==null) {
			
			JOptionPane.showMessageDialog(null,PlannerUtil.getString("please config deviceType"));
			
			return ;
		}
		//设备功率
		int power = deviceType.getPower2g()+ deviceType.getWirelessGain2g();
		//计算信号距离的常数因子
		double n=3;
		//设备信号半径
		double distance = Math.pow(10, (power - canvasModel.getMinSignToShow() - 46) / (10 * n));
		//为了让信号能全覆盖取圆的内正方形
		distance=distance/1.414*2;
		//计算距离像素
		int pixels=MapRender.meterToPixel(distance,canvasModel);
		
		int width=canvasModel.getBackImg().getWidth();
		
		int height=canvasModel.getBackImg().getHeight();

		int countx=(width+pixels-1)/pixels;
		
		int county=(height+pixels-1)/pixels;
		
		int x,y;
		
		int pixelsx=width/countx;
		
		int pixelsy=height/county;
		
		if(countx>0&&county>0){
			//自动布放设备
			for(int i=1;i<=countx;i++){
				
				x=i*pixelsx-pixelsx/2;
				
				for(int j=1;j<=county;j++){
					
					y=j*pixelsy-pixelsy/2;
					
					addDevice(new Point(x, y));
					
				}
			}
		}
	}

	public void resizeScroll() {
		// TODO Auto-generated method stub
		applicationFrame.resizeScroll();
	}
}
